// Copyright 2019 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------
// Modification history:
// feature: Unified Transport Abstraction Layer Unified Transport Abstraction Layer
// feature: support batch send
// ------------------------------------------------------------------

#ifndef SRC_CPP_RTPS_TRANSPORT_UDPCHANNELRESOURCE_H_
#define SRC_CPP_RTPS_TRANSPORT_UDPCHANNELRESOURCE_H_

#include <queue>

#include <asio.hpp>
#include <ertps/utils/LocatorAlias.h>

#include <transport/ChannelResource.h>

#ifdef EDDS_XDP_TRANSPORT_COMPILE
#include "LixdpSocketUDP.hpp"
#endif
namespace vbs {
namespace transport {

class UDPChannelResource;

class ReceiverResource;
class UDPTransportInterface;

#ifdef EDDS_XDP_TRANSPORT_COMPILE
using SocketType = vbs::transport::SocketType;
#endif

#if defined(ASIO_HAS_MOVE)
// Typedefs
typedef asio::ip::udp::socket vbsUDPSocket;
typedef vbsUDPSocket& vbsUDPSocketRef;

// UDP
inline vbsUDPSocket* getSocketPtr(vbsUDPSocket& socket) {
    return &socket;
}

inline const vbsUDPSocket* getSocketPtr(const vbsUDPSocket& socket) {
    return &socket;
}

inline vbsUDPSocket createUDPSocket(asio::io_service& io_service) {
    return asio::ip::udp::socket(io_service);
}

inline vbsUDPSocket& getRefFromPtr(vbsUDPSocket* socket) {
    return *socket;
}

#else
// Typedefs
typedef std::shared_ptr<asio::ip::udp::socket> vbsUDPSocket;
typedef vbsUDPSocket vbsUDPSocketRef;

// UDP
inline vbsUDPSocket getSocketPtr(vbsUDPSocket socket) {
    return socket;
}

inline const vbsUDPSocket& getSocketPtr(const vbsUDPSocket& socket) {
    return socket;
}

inline vbsUDPSocket createUDPSocket(asio::io_service& io_service) {
    return std::make_shared<asio::ip::udp::socket>(io_service);
}

inline vbsUDPSocket getRefFromPtr(vbsUDPSocket socket) {
    return socket;
}

#endif  // if defined(ASIO_HAS_MOVE)

class UDPChannelResource : public ChannelResource {
 public:
#ifdef EDDS_XDP_TRANSPORT_COMPILE
    UDPChannelResource(UDPTransportInterface* transport, vbsUDPSocket& socket, uint32_t maxMsgSize,
                       const vbsutil::xmlparser::Locator_t& locator, const std::string& sInterface,
                       std::shared_ptr<ReceiverResource> receiver, LixdpSocket* lixdp_socket = nullptr);
#else
    UDPChannelResource(UDPTransportInterface* transport, vbsUDPSocket& socket, uint32_t maxMsgSize,
                       const vbsutil::xmlparser::Locator_t& locator, const std::string& sInterface,
                       std::shared_ptr<ReceiverResource> receiver);
#endif

    ~UDPChannelResource() override;

    UDPChannelResource& operator=(UDPChannelResource&& channelResource) {
        socket_ = std::move(channelResource.socket_);
        return *this;
    }

    void only_multicast_purpose(const bool value) { only_multicast_purpose_ = value; }

    bool& only_multicast_purpose() { return only_multicast_purpose_; }

    bool only_multicast_purpose() const { return only_multicast_purpose_; }

#if defined(ASIO_HAS_MOVE)
    inline vbsUDPSocket* socket()
#else
    inline vbsUDPSocket socket()
#endif  // if defined(ASIO_HAS_MOVE)
    {
        return getSocketPtr(socket_);
    }

    inline void interface(const std::string& interface) { interface_ = interface; }

    inline const std::string& interface() const { return interface_; }

    inline void message_receiver(std::shared_ptr<ReceiverResource> receiver) {
        message_receiver_ = std::move(receiver);
    }

    inline ReceiverResource* message_receiver() { return message_receiver_.get(); }

    inline void disable() override { ChannelResource::disable(); }

    void clear() override;
    void release();

 protected:
    /**
     * Blocking Receive from the specified channel.
     * @param receive_buffer vector with enough capacity (not size) to accomodate a full receive buffer. That
     * capacity must not be less than the receive_buffer_size supplied to this class during construction.
     * @param receive_buffer_capacity Maximum size of the receive_buffer.
     * @param[out] receive_buffer_size Size of the received buffer.
     * @param[out] remote_locator Locator describing the remote restination we received a packet from.
     */
    bool Receive(unsigned char* receive_buffer, uint32_t receive_buffer_capacity, uint32_t& receive_buffer_size,
                 vbsutil::xmlparser::Locator_t& remote_locator);

    /**
     * Function to be called from a new thread, which takes cares of performing a blocking receive
     * operation on the ReceiveResource
     * @param input_locator - Locator that triggered the creation of the resource
     * @param thread_name - thread name
     */
    void perform_listen_operation(vbsutil::xmlparser::Locator_t input_locator, const std::string& thread_name);

 private:
    std::shared_ptr<ReceiverResource> message_receiver_;  // Associated Readers/Writers inside of MessageReceiver
    vbsUDPSocket socket_;
#ifdef EDDS_XDP_TRANSPORT_COMPILE
    LixdpSocket* lixdp_socket_;
    SocketType socket_type_;
#endif
    bool only_multicast_purpose_;
    std::string interface_;
    UDPTransportInterface* transport_;

    UDPChannelResource(const UDPChannelResource&) = delete;
    UDPChannelResource& operator=(const UDPChannelResource&) = delete;
};

}  // namespace transport
}  // namespace vbs

#endif  // SRC_CPP_RTPS_TRANSPORT_UDPCHANNELRESOURCE_H_
